package http

import (
	"bytes"
	"io"
	"net"
	"net/http"
	"net/url"
	"runtime"
	"time"

	"gitee.com/zacyuan/yuan/pkg/json"
)

const (
	ContentType     = "Content-Type"
	ApplicationJson = "application/json"
	ApplicationForm = "application/x-www-form-urlencoded"
)

type Client struct {
	*http.Client
}

func NewClient() *Client {
	return &Client{
		Client: &http.Client{
			Transport: NewTransport(),
			Timeout:   10 * time.Second,
		},
	}
}

func NewTransport() *Transport {
	return &Transport{
		Proxy: ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   3 * time.Second,
			KeepAlive: 30 * time.Second,
			DualStack: true,
		}).DialContext,
		ForceAttemptHTTP2:     false,
		MaxConnsPerHost:       runtime.GOMAXPROCS(0) * 32,
		MaxIdleConnsPerHost:   runtime.GOMAXPROCS(0) * 32,
		IdleConnTimeout:       90 * time.Second,
		TLSHandshakeTimeout:   3 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
	}
}

func (c *Client) HTTP(method, url string, data []byte, headers map[string]string, cookies map[string]string) (*Response, error) {
	req, err := NewRequest(method, url, bytes.NewReader(data))
	if err != nil {
		return nil, err
	}

	// 设置header
	for key, value := range headers {
		req.Header.Set(key, value)
	}

	// 设置cookie
	for key, value := range cookies {
		req.AddCookie(&Cookie{Name: key, Value: value, HttpOnly: true})
	}

	return c.Do(req)
}

func (c *Client) HTTPByte(method, url string, data []byte, headers map[string]string, cookies map[string]string) ([]byte, error) {
	resp, err := c.HTTP(method, url, data, headers, cookies)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	buf, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return buf, nil
}

func (c *Client) GetByte(url string) ([]byte, error) {
	resp, err := c.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	buf, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return buf, nil
}

func (c *Client) PostByte(url string, data []byte, headers map[string]string, cookies map[string]string) ([]byte, error) {
	return c.HTTPByte(MethodPost, url, data, headers, cookies)
}

func (c *Client) PostJSONByte(url string, data any, headers map[string]string, cookies map[string]string) ([]byte, error) {
	buf, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}

	if headers == nil {
		headers = map[string]string{ContentType: ApplicationJson}
	} else {
		headers[ContentType] = ApplicationJson
	}
	return c.HTTPByte(MethodPost, url, buf, headers, cookies)
}

func (c *Client) PostFormByte(url string, data url.Values, headers map[string]string, cookies map[string]string) ([]byte, error) {
	if headers == nil {
		headers = map[string]string{ContentType: ApplicationForm}
	} else {
		headers[ContentType] = ApplicationForm
	}

	return c.HTTPByte(MethodPost, url, []byte(data.Encode()), headers, cookies)
}
